/* Image Blur plugin, author @msurguy

 Usage:

 Create a set of elements that follows the following HTML structure:

 <div class="container">
   <div class="content">
   ...
   </div>
 </div>

 Add the following css:

 .container {
   overflow: hidden;
   width: 100%;
   position: relative;
 }

 .container .bg-blur-overlay {
   z-index: -1;
   position: absolute;
   width: 100%;
   height: 100%;
   background-image: url('');
   background-size: 100%;
   background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(46%, rgba(0, 0, 0, 0.08)), color-stop(59%, rgba(0, 0, 0, 0.08)), color-stop(100%, rgba(0, 0, 0, 0.9)));
   background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0.08) 46%, rgba(0, 0, 0, 0.08) 59%, rgba(0, 0, 0, 0.9) 100%);
   background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0.08) 46%, rgba(0, 0, 0, 0.08) 59%, rgba(0, 0, 0, 0.9) 100%);
   background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.08) 46%, rgba(0, 0, 0, 0.08) 59%, rgba(0, 0, 0, 0.9) 100%);
 }

 .container .bg-blur {
   z-index: -2;
   opacity: 0;
   position: absolute;
   width: 100%;
   min-height: 100%;
   height: auto;
   display: block;
   top: 0;
   left: 0;
 }

 .container .content {
  z-index: 1;
 }

 Call the plugin on the container element to add a blurred background to it:

 $('.container').backgroundBlur({
   imageURL : 'http://website.com/images/img.jpg', // URL to the image that will be used for blurring
   blurAmount : 10, // Amount of blurrines
   imageClass : 'bg-blur', // CSS class that will be applied to the image and to the SVG element,
   overlayClass : 'bg-blur-overlay', // CSS class of an element that will overlay the blur background (useful for additional effects)
   duration : 1000, // If the image needs to be faded in, how long that should take
   endOpacity : 0.6 // Specify the final opacity that the image will have
 });

 Available methods of the plugin are :

 $('.container').backgroundBlur('string'); // swap the image to another image while using initial settings for animation
 $('.container').backgroundBlur('fadeIn'); // fade in
 $('.container').backgroundBlur('fadeOut'); // fade out

 */

;(function ($) {
    'use strict';

    // IE version detection
    var ie = (function () {
        var undef,
            v = 3,
            div = document.createElement('div'),
            all = div.getElementsByTagName('i');

        while (
            div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
                all[0]
            ) {
        }

        return v > 4 ? v : undef;

    }());

    // Random ID generator
    var randomID = function () {
        return '_' + Math.random().toString(36).substr(2, 9);
    };

    //micro lib that creates SVG elements and adds attributes to it
    var SVG = {

        //namespaces
        svgns: 'http://www.w3.org/2000/svg',
        xlink: 'http://www.w3.org/1999/xlink',

        //creating of SVG element
        createElement: function (name, attrs) {
            var element = document.createElementNS(SVG.svgns, name);

            if (attrs) {
                SVG.setAttr(element, attrs);
            }
            return element;
        },

        //setting attributes
        setAttr: function (element, attrs) {
            for (var i in attrs) {
                if (i === 'href') { //path of an image should be stored as xlink:href attribute
                    element.setAttributeNS(SVG.xlink, i, attrs[i]);
                } else { //other common attribute
                    element.setAttribute(i, attrs[i]);
                }
            }
            return element;
        }
    };

    // backgroundBlur PUBLIC CLASS DEFINITION
    // ================================

    var Blur = function (element, options) {
        this.isFristRun = true;
        this.internalID = randomID();
        this.$element = $(element);
        this.element = element;
        this.options = $.extend({}, Blur.DEFAULTS, options);
        this.$overlayEl = this.createOverlay();
        this.$blurredImage = {};
        this.useVelocity = this.detectVelocity(); // If Velocity.js is present, use it for performant animation
        this.attachListeners();
        this.generateBlurredImage(this.options.imageURL);
    };

    Blur.VERSION = '0.0.1';

    Blur.DEFAULTS = {
        imageURL: '', // URL to the image
        blurAmount: 10, // Amount of blurrines
        imageClass: '', // CSS class that will be applied to the image and to the SVG element,
        overlayClass: '', // CSS class of the element that will overlay the blur image
        duration: false, // If the image needs to be faded in, how long should that take
        opacity: 1 // Specify the final opacity
    };

    Blur.prototype.detectVelocity = function () {
        return !!window.jQuery.Velocity;
    };

    Blur.prototype.attachListeners = function () {
        this.$element.on('ui.blur.loaded', $.proxy(this.fadeIn, this));
        this.$element.on('ui.blur.unload', $.proxy(this.fadeOut, this));
    };

    Blur.prototype.fadeIn = function () {
        if (this.options.duration && this.options.duration > 0) {
            if (this.useVelocity) {
                this.$blurredImage
                    .velocity({
                        opacity: this.options.opacity
                    }, {
                        duration: this.options.duration
                    });
            } else {
                this.$blurredImage
                    .animate({
                        opacity: this.options.opacity
                    }, {
                        duration: this.options.duration
                    });
            }
        }
    };

    Blur.prototype.fadeOut = function () {
        if (this.options.duration && this.options.duration > 0) {
            if (this.useVelocity) {
                this.$blurredImage
                    .velocity({
                        opacity: 0
                    }, {
                        duration: this.options.duration
                    });
            } else {
                this.$blurredImage
                    .animate({
                        opacity: 0
                    }, {
                        duration: this.options.duration
                    });
            }

        } else {
            this.$blurredImage.css({'opacity': 0});
        }
    };


    Blur.prototype.resize = function () {
        this.$blurredImage.css({'width': (this.$element.width() + 40) + 'px', 'height': (this.$element.height() + 40) + 'px'});
    };

    Blur.prototype.generateBlurredImage = function (url) {
        var $previousImage = this.$blurredImage;
        this.internalID = randomID();

        if ($previousImage.length > 0) {
            if (this.options.duration && this.options.duration > 0) {
                if (this.useVelocity) {
                    $previousImage.first().velocity({opacity: 0}, {
                        duration: this.options.duration,
                        complete: function () {
                            $previousImage.remove();
                        }
                    });
                } else {
                    $previousImage.first().animate({opacity: 0}, {
                        duration: this.options.duration,
                        complete: function () {
                            $previousImage.remove();
                        }
                    });
                }
            } else {
                $previousImage.remove();
            }

        }
        if (!ie) {
            this.$blurredImage = this.createSVG(url, this.$element.width() + 40, this.$element.height() + 40);
        } else {
            this.$blurredImage = this.createIMG(url, this.$element.width() + 40, this.$element.height() + 40);
        }
    };

    Blur.prototype.createOverlay = function () {
        if (this.options.overlayClass && this.options.overlayClass !== '') {
            return $('<div></div>').prependTo(this.$element).addClass(this.options.overlayClass);
        }

        return false;
    };

    Blur.prototype.createSVG = function (url, width, height) {
        var that = this;
        var svg = SVG.createElement('svg', { //our SVG element
            xmlns: SVG.svgns,
            version: '1.1',
            width: width,
            height: height,
            id: 'blurred' + this.internalID,
            'class': this.options.imageClass,
            viewBox: '0 0 ' + width + ' ' + height,
            preserveAspectRatio: 'xMidYMid slice'
        });

        var filterId = 'blur' + this.internalID; //id of the filter that is called by image element
        var filter = SVG.createElement('filter', { //filter
            id: filterId
        });

        var gaussianBlur = SVG.createElement('feGaussianBlur', { // gaussian blur element
            'in': 'SourceGraphic', //"in" is keyword. Opera generates an error if we don't put quotes
            stdDeviation: this.options.blurAmount // intensity of blur
        });

        var image = SVG.createElement('image', { //The image that uses the filter of blur
            x: 0,
            y: 0,
            width: width,
            height: height,
            'externalResourcesRequired': 'true',
            href: url,
            style: 'filter:url(#' + filterId + ')', //filter link
            preserveAspectRatio: 'xMidYMid slice'
        });

        image.addEventListener('load', function () {
            that.$element.trigger('ui.blur.loaded');
        }, true);

        image.addEventListener('SVGLoad', function () {
            that.$element.trigger('ui.blur.loaded');
        }, true);

        filter.appendChild(gaussianBlur); //adding the element of blur into the element of filter
        svg.appendChild(filter); //adding the filter into the SVG
        svg.appendChild(image); //adding an element of an image into the SVG
        var $svg = $(svg);

        // Ensure that the image is shown after duration + 100 msec in case the SVG load event didn't fire or took too long
        if (that.options.duration && that.options.duration > 0) {
            if (that.isFristRun) {
                that.isFristRun = false;
            } else {
                $svg.css({opacity: 0});
                window.setTimeout(function () {
                    if ($svg.css('opacity') === '0') {
                        $svg.css({opacity: 1});
                    }
                }, this.options.duration + 100);
            }
        }
        this.element.insertBefore(svg, this.element.firstChild);
        return $svg;
    };

    Blur.prototype.createIMG = function (url, width, height) {
        var that = this;
        var $originalImage = this.prependImage(url);
        var newBlurAmount = ((this.options.blurAmount * 2) > 100) ? 100 : (this.options.blurAmount * 2);
        // apply special CSS attributes to the image to blur it
        $originalImage.css({
            //filter property; here the intensity of blur multipied by two is around equal to the intensity in common browsers.
            filter: 'progid:DXImageTransform.Microsoft.Blur(pixelradius=' + newBlurAmount + ') ',
            //aligning of the blurred image by vertical and horizontal
            top: -this.options.blurAmount * 2.5,
            left: -this.options.blurAmount * 2.5,
            width: width + (this.options.blurAmount * 2.5),
            height: height + (this.options.blurAmount * 2.5)
        })
            .attr('id', 'blurred' + this.internalID);

        $originalImage.load(function () {
            that.$element.trigger('ui.blur.loaded');
        });

        // Ensure that the image is shown after duration + 100 msec in case the image load event didn't fire or took too long
        if (this.options.duration && this.options.duration > 0) {
            window.setTimeout(function () {
                if ($originalImage.css('opacity') === '0') {
                    $originalImage.css({opacity: 1});
                }
            }, this.options.duration + 100);
        }
        return $originalImage;
    };

    Blur.prototype.prependImage = function (url) {
        var $image;
        var $imageEl = $('<img src="' + url + '" />');
        if (this.$overlayEl) {
            $image = $imageEl.insertBefore(this.$overlayEl).attr('id', this.internalID).addClass(this.options.imageClass);
        } else {
            $image = $imageEl.prependTo(this.$element).attr('id', this.internalID).addClass(this.options.imageClass);
        }

        return $image;
    };
    // backgroundBlur PLUGIN DEFINITION
    // ==========================

    function Plugin(option) {
        return this.each(function () {
            var $this = $(this);
            var data = $this.data('plugin.backgroundBlur');
            var options = $.extend({}, Blur.DEFAULTS, $this.data(), typeof option === 'object' && option);

            if (!data) {
                $this.data('plugin.backgroundBlur', (data = new Blur(this, options)));
            }
            if (option === 'fadeIn') {
                data.fadeIn();
            } else if (option === 'fadeOut') {
                data.fadeOut();
            } else if (option === 'resize') {
                data.resize();
            } else if (typeof option === 'string') {
                data.generateBlurredImage(option);
            }
        });
    }

    var old = $.fn.backgroundBlur;

    $.fn.backgroundBlur = Plugin;
    $.fn.backgroundBlur.Constructor = Blur;

    // NO CONFLICT
    // ====================

    $.fn.backgroundBlur.noConflict = function () {
        $.fn.backgroundBlur = old;
        return this;
    };

})(jQuery);
